<?php

namespace System\library\error;

use System\InitSystem;
use System\library\mvc\controller\Dispatcher;
use System\library\pattern\Singleton;

/**
 * <b>Class</b> :        ErrorManager<br>
 * <b>Fonction</b> :     Permet la gestion des erreurs générées par PHP
 * <br>
 * @version              1.0
 * @author               Dread <dreadlokeur@gmail.com>
 * @license              Licence publique générale GNU http://www.gnu.org/licenses/gpl.html
 * @copyright            Copyright 2011 - MidichloriansPHP and contributors
 * @package              System
 * @subpackage           library\error
 *
 * @see System\library\pattern\Singleton
 * @see \SplSubject
 */
class ErrorManager extends Singleton implements \SplSubject {

    private $_observers; //object SplObjectStorage
    private $_error; // object
    private $_initializedError = false;
    private $_clearErrorAfterSending = true;

    public function start($catchFatal = true, $displayErrorsOff = false) {
        if ($catchFatal)
            register_shutdown_function(array($this, 'fatalErrorHandler'));
        if ($displayErrorsOff)
            ini_set('display_errors', 0);
        set_error_handler(array($this, 'errorHandler'));
        return $this;
    }

    public function stop() {
        restore_error_handler();
    }

    public function attach(\SplObserver $observer) {
        if ($this->_observers->contains($observer))
            throw new \Exception('Observer "' . $observer . '" is already attached');
        $this->_observers->attach($observer);
        return $this;
    }

    public function detach(\SplObserver $observer) {
        if (!$this->_observers->contains($observer))
            throw new \Exception('Observer "' . $observer . '" don\'t exist');
        $this->_observers->detach($observer);
        return $this;
    }

    public function notify() {
        if ($this->_observers->count()) {
            foreach ($this->_observers as $observer)
                $observer->update($this);
        }
        // Show internal server error (500)
        if (!InitSystem::isDebug())
            Dispatcher::getInstance()->show500();
        // Clear error for avoid multiple call
        if ($this->_clearErrorAfterSending)
            unset($this->_error);

        exit;
    }

    public function errorHandler($code, $message, $file, $line) {
        $this->_setError($code, $message, $file, $line);
        $this->notify();
        // Do not execute the PHP error handler
        return true;
    }

    public function fatalErrorHandler() {
        $lastError = error_get_last();
        if ($lastError['type'] === E_ERROR) {
            $this->_setError(E_ERROR, $lastError['message'], $lastError['file'], $lastError['line'], 'E_FATAL');
            $this->notify();
            return true;
        }
    }

    public function getError() {
        if (!$this->_initializedError)
            throw new \Exception('Error must be initialized with "_setError() method"');
        return $this->_error;
    }

    public function setClearErrorAfterSending($bool) {
        if (!is_bool($bool))
            throw new \Exception('clearExceptionAfterSending parameter must be a boolean');
        $this->_clearErrorAfterSending = $bool;
        return $this;
    }

    protected function __construct() {
        $this->_observers = new \SplObjectStorage();
    }

    private function _setError($code, $message, $file, $line, $type = null) {
        $type = ($type !== null) ? $this->_getErrorType($type) : $this->_getErrorType($code);
        $error = new \stdClass();
        $error->code = $code;
        $error->type = $type;
        $error->message = $message;
        $error->file = $file;
        $error->line = $line;

        $this->_error = $error;
        $this->_initializedError = true;
    }

    private function _getErrorType($errCode) {
        switch ($errCode) {
            case 'E_FATAL':
                $type = 'Fatal erreur';
                break;
            case E_ERROR:
                $type = 'Erreur';
                break;
            case E_WARNING:
                $type = 'Alerte';
                break;
            case E_PARSE:
                $type = 'Erreur d\'analyse';
                break;
            case E_NOTICE:
                $type = 'Note';
                break;
            case E_CORE_ERROR:
                $type = 'Core Error';
                break;
            case E_CORE_WARNING:
                $type = 'Core Warning';
                break;
            case E_COMPILE_ERROR:
                $type = 'Compile Error';
                break;
            case E_COMPILE_WARNING:
                $type = 'Compile Warning';
                break;
            case E_USER_ERROR:
                $type = 'Erreur spécifique';
                break;
            case E_USER_WARNING:
                $type = 'Alerte spécifique';
                break;
            case E_USER_NOTICE:
                $type = 'Note spécifique';
                break;
            case E_STRICT:
                $type = 'Runtime Notice';
                break;
            case E_RECOVERABLE_ERROR:
                $type = 'Catchable Fatal Error';
                break;
            default:
                $type = 'Type d\'erreur inconnue';
                break;
        }
        return $type;
    }

}

?>